#include "Widget.hpp"

namespace red {
	namespace ui {
		Widget::Widget(const Vector2F &pPosition, const Vector2F &pSize)
		:	sf::Drawable(RedToSFMLVector2F(pPosition)),
			mParent(0),
			mRectangle(pPosition, pSize),
			mAlignment(Absolute),
			mParentOffset(Vector2F::ZERO),
			mUseBorder(false),
			mBorderWidth(1),
			mBorderColor(Color(0,0,0,0)),
			mEnabled(true),
			mVisible(true),
			mFocusable(true)
		{

		}

		Widget::~Widget(){
		}

		void Widget::Add(Widget &pWidget){
			// Search if this widget is already present in children
			if(FindChild(&pWidget) == mChildren.end()){
				mChildren.push_back(&pWidget);
				pWidget.mParent = this;
				pWidget.OnUpdate(Position);
			}
		}

		void Widget::Remove(Widget* pWidget){
			WidgetList::iterator it = FindChild(pWidget);

			if(it != mChildren.end()){
				pWidget->mParent = 0;
				mChildren.erase(it);
			}
		}

		void Widget::ChangeDepth( WidgetDepth pParam ){
			if(mParent){
				// Retrieve the current widget in its parent childen
				WidgetList& parentChildren = mParent->mChildren;
				u32 i;

				for(i = 0; i < parentChildren.size(); ++i)
					if(parentChildren[i] == this)
						break;

				// Move it in the list to its new position
				switch(pParam){
				case AboveAll:
					parentChildren.erase(parentChildren.begin() + i);
					parentChildren.push_back(this);
					break;
				case BelowAll:
					parentChildren.erase(parentChildren.begin() + i);
					parentChildren.insert(parentChildren.begin(), this);
					break;
				case Down:
					if(i > 0)
						std::swap(parentChildren[i], parentChildren[i-1]);
					break;
				case Up:
					if(i < parentChildren.size() - 1)
						std::swap(parentChildren[i], parentChildren[i+1]);
					break;
				}
			}
		}

		void Widget::UpdateBorder(){
			if(mUseBorder){
				// If we want a border, update the BorderRectangle for drawing from the borderwidth
				Vector2F width(mBorderWidth, mBorderWidth);
				mBorderRectangle.SetPosition(mRectangle.Origin() - width);
				mBorderRectangle.SetSize(mRectangle.Size() +  width);
			}
		}

		void Widget::SetAlignment(WidgetAlignment pAlignment, const Vector2F &pParentOffset){
			mAlignment = pAlignment;
			mParentOffset = pParentOffset;
			OnUpdate(Position);
		}

		bool Widget::SetAlignmentFromString(const std::string &pAlignment, const Vector2F &pParentOffset){
			if(pAlignment == "absolute")
				mAlignment = Absolute;
			else if(pAlignment == "top")
				mAlignment = Top;
			else if(pAlignment == "topleft")
				mAlignment = TopLeft;
			else if(pAlignment == "topright")
				mAlignment = TopRight;
			else if(pAlignment == "center")
				mAlignment = Center;
			else if(pAlignment == "left")
				mAlignment = Left;
			else if(pAlignment == "right")
				mAlignment = Right;
			else if(pAlignment == "bottom")
				mAlignment = Bottom;
			else if(pAlignment == "bottomleft")
				mAlignment = BottomLeft;
			else if(pAlignment == "bottomright")
				mAlignment = BottomRight;
			else
				return false;

			mParentOffset = pParentOffset;
			OnUpdate(Position);

			return true;
		}

		void Widget::CalculatePosition(){
			// Switch Alignment and widget position from it
			if(mParent && mAlignment != Absolute){
				switch (mAlignment){
				case Left:
					SetPosition(Vector2F(0, mParent->GetSize().y * .5f - GetSize().y * .5f));
					break;
				case Top:
					SetPosition(Vector2F(mParent->GetSize().x * .5f - GetSize().x * .5f, 0));
					break;
				case Right:
					SetPosition(Vector2F(mParent->GetSize().x - GetSize().x, mParent->GetSize().y * .5f - GetSize().y * .5f));
					break;
				case Bottom:
					SetPosition(Vector2F(mParent->GetSize().x * .5f - GetSize().x * .5f, mParent->GetSize().y - GetSize().y));
					break;
				case Center:
					SetPosition(Vector2F(mParent->GetSize().x * .5f - GetSize().x * .5f, mParent->GetSize().y * .5f - GetSize().y * .5f));
					break;
				case TopLeft:
					SetPosition(Vector2F(0, 0));
					break;
				case TopRight:
					SetPosition(Vector2F(mParent->GetSize().x - GetSize().x, 0));
					break;
				case BottomLeft:
					SetPosition(Vector2F(0, mParent->GetSize().y - GetSize().y));
					break;
				case BottomRight:
					SetPosition(Vector2F(mParent->GetSize().x - GetSize().x, mParent->GetSize().y - GetSize().y));
					break;
				default:
					break;
				}

				// Move the widget form its parent offset if it has one
				if(mParentOffset.x != 0 && mParentOffset.y != 0)
					SetPosition(GetPosition() + mParentOffset);
			}
		}


		void Widget::OnUpdate(WidgetParameter pParam){
			switch(pParam){
			case Position:
				CalculatePosition();
				UpdateBorder();
				break;

			case Size:
				UpdateBorder();
				for(WidgetList::iterator it = mChildren.begin(); it != mChildren.end(); ++it)
					(*it)->OnUpdate(Position);
				break;
			default:
				break;
			}
		}

		void Widget::DrawBorder(sf::Renderer &pRenderer) const{
			pRenderer.SetColor(RedToSFMLColor(mBorderColor));

			pRenderer.Begin(sf::Renderer::QuadList);
			// Top Border
			pRenderer.AddVertex(-mBorderWidth,-mBorderWidth);
			pRenderer.AddVertex(-mBorderWidth, 0);
			pRenderer.AddVertex(mBorderRectangle.Width(), 0);
			pRenderer.AddVertex(mBorderRectangle.Width(), -mBorderWidth);

			// Left Border
			pRenderer.AddVertex(-mBorderWidth,-mBorderWidth);
			pRenderer.AddVertex(-mBorderWidth, mBorderRectangle.Height());
			pRenderer.AddVertex(0, mBorderRectangle.Height());
			pRenderer.AddVertex(0, -mBorderWidth);

			// Bottom Border
			pRenderer.AddVertex(-mBorderWidth, mRectangle.Height());
			pRenderer.AddVertex(-mBorderWidth, mBorderRectangle.Height());
			pRenderer.AddVertex(mBorderRectangle.Width(), mBorderRectangle.Height());
			pRenderer.AddVertex(mBorderRectangle.Width(), mRectangle.Height());

			// Right Border
			pRenderer.AddVertex(mRectangle.Width(),-mBorderWidth);
			pRenderer.AddVertex(mRectangle.Width(), mBorderRectangle.Height());
			pRenderer.AddVertex(mBorderRectangle.Width(), mBorderRectangle.Height());
			pRenderer.AddVertex(mBorderRectangle.Width(), -mBorderWidth);

			pRenderer.End();
		}

		void Widget::OnRender(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const{
			if(mUseBorder){
				// If border used, draw a first shape with border color, then an other with widget color
				DrawBorder(pRenderer);
				pRenderer.SetColor(GetColor());
				pTarget.Draw(sf::Shape::Rectangle(0, 0, mRectangle.Width(), mRectangle.Height(), GetColor()));
				
			}else{
				pRenderer.SetColor(GetColor());
				pTarget.Draw(sf::Shape::Rectangle(0,0, mRectangle.Width(), mRectangle.Height(), GetColor()));
			}
		}

		void Widget::Render(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const{
			OnRender(pTarget, pRenderer);
			RenderChildren(pTarget, pRenderer);
		}

		void Widget::RenderChildren(sf::RenderTarget &pTarget, sf::Renderer &pRenderer) const{
			for(std::vector<Widget*>::const_iterator it = mChildren.begin(); it != mChildren.end(); ++it){
				if((*it)->IsVisible())
					pTarget.Draw(*(*it));
			}
		}

		WidgetList::iterator Widget::FindChild(const Widget* pChild){
			for(WidgetList::iterator it = mChildren.begin(); it != mChildren.end(); ++it){
				if((*it) == pChild)
					return it;
			}
			return mChildren.end();
		}

		void Widget::UseBorder( bool pVal, f32 pWidth, const Color &pColor ){  
			mUseBorder = pVal;

			if(mUseBorder){
				// If we use border, we set its width if one is given and its color if one is given
				if(pWidth > 0)
					mBorderWidth = pWidth;
				if(mBorderColor.A() == 0 || pColor != Color::Black)
					mBorderColor = pColor;

				UpdateBorder();	   
			}
		}

	}
}
